﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DemoForm
{
    public partial class _2048 : Form
    {
        /// <summary>
        /// 得分
        /// </summary>
        private int Score = 0;
        // 当前分布的map对应
        private int[,] Map;
        private Block[,] BMap;
        // 显示用背景色列表
        private Color[] ColorList;

        public _2048()
        {
            InitializeComponent();
            InitSet();
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void InitSet()
        {
            Map = new int[4, 4];
            BMap = new Block[4, 4];
            ColorList = new Color[]
            {
                Color.LightSeaGreen,
                Color.LightSkyBlue,
                Color.Aquamarine,
                Color.Beige,
                Color.Lime,
                Color.Coral,
                Color.Chartreuse,
                Color.Cyan,
                Color.MistyRose,
                Color.DarkOrange,
                Color.DarkTurquoise,
                Color.Gold,
                Color.GreenYellow,
                Color.LightCoral,
                Color.Yellow
            };
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Down:
                case Keys.Up:
                case Keys.Left:
                case Keys.Right:
                    Move(keyData);
                    break;
                default:
                    break;
            }
            return true;
        }

        private void _2048_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Down:
                case Keys.Up:
                case Keys.Left:
                case Keys.Right:
                    Move(e.KeyCode);
                    break;
                default:
                    break;
            }
        }

        private void Move(Keys keyCode)
        {
            // 先模拟移动
            if (keyCode == Keys.Down)
            {
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 2; j >= 0; j--)
                    {
                        BMap[i, j]?.Move(this.Map, keyCode, ref Score);
                    }
                }
            }
            else if (keyCode == Keys.Up)
            {
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 1; j < 4; j++)
                    {
                        BMap[i, j]?.Move(this.Map, keyCode, ref Score);
                    }
                }
            }
            else if (keyCode == Keys.Left)
            {
                for (int j = 0; j < 4; j++)
                {
                    for (int i = 1; i < 4; i++)
                    {
                        BMap[i, j]?.Move(this.Map, keyCode, ref Score);
                    }
                }
            }
            else if (keyCode == Keys.Right)
            {
                for (int j = 0; j < 4; j++)
                {
                    for (int i = 2; i >= 0; i--)
                    {
                        BMap[i, j]?.Move(this.Map, keyCode, ref Score);
                    }
                }
            }

            // 执行动画
            if (keyCode == Keys.Down)
            {
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 2; j >= 0; j--)
                    {
                        if (BMap[i, j]?.NextLocation != null)
                        {
                            BLL.AnimationHelper.AddMove(BMap[i, j].button, GetLocation(BMap[i, j].NextLocation.X, BMap[i, j].NextLocation.Y));
                        }
                    }
                }
            }
            else if (keyCode == Keys.Up)
            {
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 1; j < 4; j++)
                    {
                        if (BMap[i, j]?.NextLocation != null)
                        {
                            BLL.AnimationHelper.AddMove(BMap[i, j].button, GetLocation(BMap[i, j].NextLocation.X, BMap[i, j].NextLocation.Y));
                        }
                    }
                }
            }
            else if (keyCode == Keys.Left)
            {
                for (int j = 0; j < 4; j++)
                {
                    for (int i = 1; i < 4; i++)
                    {
                        if (BMap[i, j]?.NextLocation != null)
                        {
                            BLL.AnimationHelper.AddMove(BMap[i, j].button, GetLocation(BMap[i, j].NextLocation.X, BMap[i, j].NextLocation.Y));
                        }
                    }
                }
            }
            else if (keyCode == Keys.Right)
            {
                for (int j = 0; j < 4; j++)
                {
                    for (int i = 2; i >= 0; i--)
                    {
                        if (BMap[i, j]?.NextLocation != null)
                        {
                            BLL.AnimationHelper.AddMove(BMap[i, j].button, GetLocation(BMap[i, j].NextLocation.X, BMap[i, j].NextLocation.Y));
                        }
                    }
                }
            }

            BLL.AnimationHelper.Start(() =>
            {
                // 比较map和BMap的值，将合并的button重新设置
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        var mV = Map[i, j];
                        var bV = BMap[i, j]?.Num;
                        if (mV != bV)
                        {
                            if (BMap[i, j] != null && BMap[i, j].button != null)
                            {
                                panel1.Controls.Remove(BMap[i, j].button);
                            }

                            if (mV == 0)
                            {
                                BMap[i, j] = null;
                            }
                            else
                                CreateNew(i, j, mV);
                        }
                    }
                }
                // 再添加一个
                AddNew();
            });
        }

        private object LockObj = new object();

        private void CreateNew(int x, int y, int mV)
        {
            lock (LockObj)
            {
                this.Invoke(new Action(() =>
                {
                    var blk = CreateBlock(mV);
                    blk.X = x;
                    blk.Y = y;
                    blk.button.Location = GetLocation(x, y);
                    panel1.Controls.Add(blk.button);
                    BMap[x, y] = blk;
                }));
            }
        }

        private void AddNew()
        {
            lock (LockObj)
            {
                this.Invoke(new Action(() =>
                {
                    var num = DateTime.Now.Millisecond % 2 + 1;
                    var blk = CreateBlock(num);

                    Random rand = new Random();
                    while (true)
                    {
                        var x = rand.Next(0, 4);
                        var y = rand.Next(0, 4);
                        if (Map[x, y] == 0)
                        {
                            blk.X = x;
                            blk.Y = y;
                            blk.button.Location = GetLocation(x, y);
                            panel1.Controls.Add(blk.button);
                            Map[x, y] = num;
                            BMap[x, y] = blk;
                            break;
                        }
                    }
                }));
            }
        }

        private Point GetLocation(int x, int y)
        {
            return new Point(5 + x * 55, 5 + y * 55);
        }

        private Block CreateBlock(int num)
        {
            Button button = new Button();
            button.Text = num.ToString();
            button.TextAlign = ContentAlignment.MiddleCenter;
            button.Font = new Font("黑体", 12, FontStyle.Bold);
            button.Size = new Size(50, 50);
            button.ForeColor = Color.Black;
            int index = (int)Math.Log(num, 2);
            button.BackColor = ColorList[index];

            return new Block() { button = button, Num = num };
        }

        private void _2048_Load(object sender, EventArgs e)
        {
            for (int i = 0; i < 4; i++)
            {
                AddNew();
            }
        }
    }

    public class Block
    {
        public int X;
        public int Y;
        public int Num;
        public Button button;
        public Point NextLocation;

        public void Move(int[,] map, Keys mKey,ref int Score)
        {
            int nx = X;
            int nY = Y;
            while (true)
            {
                SetMove(ref nx, ref nY, mKey);
                if (nx < 0 || nx >= 4 || nY < 0 || nY >= 4)
                    return;

                var value = map[nx, nY];
                if (value == 0)
                {
                    map[X, Y] = 0;
                    map[nx, nY] = this.Num;
                    X = nx;
                    Y = nY;
                    NextLocation = new Point(nx, nY);
                    continue;
                }
                else if (value == this.Num)
                {
                    map[X, Y] = 0;
                    map[nx, nY] = this.Num * 2;
                    Score += this.Num;
                    NextLocation = new Point(nx, nY);
                    return;
                }
                else
                    return;
            }
        }

        private void SetMove(ref int nX, ref int nY, Keys mKey)
        {
            if (mKey == Keys.Down)
                nY++;
            else if (mKey == Keys.Up)
                nY--;
            else if (mKey == Keys.Left)
                nX--;
            else if (mKey == Keys.Right)
                nX++;
        }
    }
}
